package de.dnb.gnd.parser.tag;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import de.dnb.basics.filtering.FilterUtils;
import de.dnb.basics.filtering.IPredicate;
import de.dnb.gnd.parser.Indicator;
import de.dnb.gnd.parser.Repeatability;
import de.dnb.gnd.parser.line.GNDLineFactory;
import de.dnb.gnd.parser.line.LineFactory;

/**
 * Hat mehrere Aufgaben:
 * 	-	Beschreibung der Tags zu liefern und eine Übersetzung pica <-> pica+
 * 		zu liefern.
 * 	-	Die zulässigen Unterfelder zu liefern.
 * 
 * Da von {@link GNDTagDB} noch Änderungen vorgenommen werden, sind nicht alle
 * Membervariablen final. Trotzdem immutable, daher kein clone().
 * 
 * @author baumann
 *
 */
public abstract class GNDTag extends Tag {

	/**
	 *  Tags, von denen Unterfelder übernommen werden.
	 *  z.B. 5102 von 5100.
	 *  Das können aber auch künstlich gebildete Tags wie 5XX sein. 
	 *  Das erleichtert die
	 *  Wartbarkeit, wenn Änderungen an einer ganzen Gruppe vorgenommen werden.
	 */

	public static final String OHNE = "-ohne-";

	// ------- Hilfsstrukturen zur Speicherung von Zwischenergebnissen:
	/*
	 * Die Indikatoren, die Deskriptionszeichen (Präfixe) enthalten 
	 * (z.B. ", " bei 100 $d)
	 */
	private Set<Indicator> descriptionStrings;

	//@formatter:off
	GNDTag(
			final String pica3,
			final String picaPlus,
			final String german,
			final Repeatability repeatability,
			final String marc,
			final String english) {
		super(pica3, picaPlus, german, repeatability, marc, english);
	}
	
	GNDTag(
		final String pica3,
		final String picaPlus,
		final String german,
		final Repeatability repeatability,
		final String marc,
		final String english,
		final String aleph) {
	super(pica3, picaPlus, german, repeatability, marc, english, aleph);
}

	@Override
	public abstract GNDLineFactory getLineFactory();
	//@formatter:on
	
	protected Indicator defaultFirst = null;

	final void addDefaultFirst(final Indicator indicator) {
		add(indicator);
		this.defaultFirst = indicator;
	}

	@Override
	public final Indicator getIndicator(final char ind) {
		return getIndicator(ind, true);
	}

	/**
	 * Liefert die Unterfelder, die auch im pica3-Format durch 
	 * Deskriptionszeichen ersetzt werden müssen. (Gilt nur für GND). 
	 * Liefert nicht $9, da hierfür eine Sonderbehandlung beim Parsen
	 * erforderlich ist.
	 * Liefert auch die relationierten Unterfelder (wegen Expansion).
	 * 
	 * @return Set != null, eventuell leer.
	 */
	public final Set<Indicator> getDescriptionStrings() {
		if (descriptionStrings == null) {

			descriptionStrings = new HashSet<Indicator>(getAllIndicators());

			/**
			 * null-Präfixe und $9 entfernen.
			 */
			IPredicate<Indicator> predicate = new IPredicate<Indicator>() {
				@Override
				public boolean accept(final Indicator indicator) {
					return (indicator.prefix != null)
						&& indicator != TagDB.DOLLAR_9
						&& indicator != TagDB.DOLLAR_8;
				}
			};
			FilterUtils.filter(descriptionStrings, predicate);

		}
		return Collections.unmodifiableSet(descriptionStrings);
	}

	/**
	 * Liefert das Unterfeld, das im pica3-Format am Anfang
	 * stehen darf/muss.
	 * 
	 * @return Indicator eventuell null.
	 */
	@Override
	public final Indicator getDefaultFirst() {
		Indicator sub = this.defaultFirst;
		if (sub != null)
			return sub;
		for (Tag tag : getInherited()) {
			sub = tag.getDefaultFirst();
			if (sub != null)
				return sub;
		}
		if (related == null)
			return null;
		else
			return related.getDefaultFirst();
	}

	public static void main(String[] args) {
		GNDTag tag = (GNDTag) GNDTagDB.getDB().findTag("500");
		System.out.println(tag.related);
		System.out.println(tag.getInherited());
		System.out.println(tag.indicatorMap);
		System.out.println(tag.getIndicatorMap(false));
	}

}
